package com.tuzhi.base.jpa.repository;

import com.tuzhi.base.jpa.domain.QueryConditionBO;
import com.tuzhi.base.jpa.domain.SortConditionBO;
import com.tuzhi.base.jpa.domain.WhereConditionBO;
import com.tuzhi.base.jpa.enums.AndOrOperate;
import com.tuzhi.base.jpa.enums.SortOrderEnum;
import com.tuzhi.base.jpa.healper.PredicateHelper;
import com.tuzhi.util.ListTzUtil;
import com.tuzhi.util.StringTzUtil;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@NoRepositoryBean
public interface BaseRepo<T, ID> extends JpaRepository<T, ID>, JpaSpecificationExecutor<T> {

    default Page<T> findPage(PageRequest pageRequest, List<QueryConditionBO> list) {
        Specification<T> specification = buildSpecification(list);
        Page<T> rs = findAll(specification, pageRequest);
        return rs;
    }

    default Page<T> findPage(PageRequest pageRequest, List<QueryConditionBO> list, String sortName) {
        Specification<T> specification = buildSpecification(list);
        pageRequest = pageRequest.withSort(Sort.by(sortName));
        Page<T> rs = findAll(specification, pageRequest);
        return rs;
    }

    default Page<T> findPage(PageRequest pageRequest, List<QueryConditionBO> list, Sort sort) {
        Specification<T> specification = buildSpecification(list);
        pageRequest = pageRequest.withSort(sort);
        Page<T> rs = findAll(specification, pageRequest);
        return rs;
    }

    default Page<T> findPage(PageRequest pageRequest, List<QueryConditionBO> list, List<SortConditionBO> sortList) {
        Specification<T> specification = buildSpecification(list);
        if (CollectionUtils.isEmpty(sortList)) {
        } else {
            pageRequest = pageRequest.withSort(buildSort(sortList));
        }
        return findAll(specification, pageRequest);
    }

    default Page<T> findPage(PageRequest pageRequest, WhereConditionBO whereConditionBO) {
        List<QueryConditionBO> queryConditionBOList = new ArrayList<>();
        List<SortConditionBO> sortConditionBOList = new ArrayList<>();
        if (whereConditionBO != null) {
            if (whereConditionBO.getQueryList() != null) {
                queryConditionBOList = whereConditionBO.getQueryList();
            }
            if (whereConditionBO.getSortList() != null) {
                sortConditionBOList = whereConditionBO.getSortList();
            }
        }
        return findPage(pageRequest, queryConditionBOList, sortConditionBOList);
    }

    default List<T> findAll(List<QueryConditionBO> list) {
        Specification<T> specification = buildSpecification(list);
        List<T> rs = findAll(specification);
        return rs;
    }

    default List<T> findAll(List<QueryConditionBO> list, String sortName) {
        return findAll(list, Sort.by(sortName));
    }

    default List<T> findAll(List<QueryConditionBO> list, Sort sort) {
        Specification<T> specification = buildSpecification(list);
        List<T> rs = findAll(specification, sort);
        return rs;
    }

    default List<T> findAll(List<QueryConditionBO> list, List<SortConditionBO> sortList) {
        Specification<T> specification = buildSpecification(list);
        List<T> rs = null;
        if (CollectionUtils.isEmpty(sortList)) {
            rs = findAll(specification);
        } else {
            Sort sort = buildSort(sortList);
            rs = findAll(specification, sort);
        }
        return rs;
    }

    default List<T> findAll(WhereConditionBO whereConditionBO) {
        return findAll(whereConditionBO.getQueryList(), whereConditionBO.getSortList());
    }

    default T findOne(List<QueryConditionBO> list) {
        Specification<T> specification = buildSpecification(list);
        Optional<T> optional = findOne(specification);
        if (optional.isPresent()) {
            return optional.get();
        } else {
            return null;
        }
    }

    default T findOne(WhereConditionBO whereConditionBO) {
        List<T> list = findAll(whereConditionBO);
        if (ListTzUtil.isNotEmpty(list)) {
            return list.get(0);
        }
        return null;
    }

    default Sort buildSort(List<SortConditionBO> sortList) {
        Sort sort = null;
        if (CollectionUtils.isEmpty(sortList)) {
        } else {
            List<Sort.Order> orders = new ArrayList<>();
            for (SortConditionBO sortOrderConditon : sortList) {
                if (sortOrderConditon.getSortOrderEnum() == SortOrderEnum.ACE) {
                    orders.add(Sort.Order.asc(sortOrderConditon.getPropertyName()));
                } else {
                    orders.add(Sort.Order.desc(sortOrderConditon.getPropertyName()));
                }
            }
            sort = (Sort.by(orders));
        }
        return sort;
    }

    default Sort buildSort(String orderName, String orderType) {
        Sort sort = null;
        if (StringTzUtil.isNotBlankNull(orderName)) {
            if (SortOrderEnum.DESC.name().equalsIgnoreCase(orderType)) {
                sort = Sort.by(Sort.Order.desc(orderName));
            } else {
                sort = Sort.by(Sort.Order.asc(orderName));
            }
        }
        return sort;
    }

    default Specification<T> buildSpecification1(List<QueryConditionBO> list) {
        Specification<T> specification = new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                List<Predicate> rs = new ArrayList<Predicate>();
                for (QueryConditionBO queryConditon : list) {
                    rs.add(PredicateHelper.buildPredicate(queryConditon, root, criteriaBuilder));
                }
                //新建一个数组，这个对象代表一个查询条件
                Predicate[] p = new Predicate[rs.size()];

                //将list对象转化为p类型数组，加载cb中
                return criteriaBuilder.and(rs.toArray(p));
            }
        };
        return specification;
    }

    default Specification<T> buildSpecification(List<QueryConditionBO> list) {
        Specification<T> specification = new Specification<T>() {
            @Override
            public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                Predicate prePredicate = null;
                for (QueryConditionBO queryConditon : list) {
                    Predicate predicate = PredicateHelper.buildPredicate(queryConditon, root, criteriaBuilder);
                    if (prePredicate == null) {
                        prePredicate = predicate;
                    } else {
                        if (AndOrOperate.and.getName().equalsIgnoreCase(queryConditon.getAndOrOperate().getName())) {
                            prePredicate = criteriaBuilder.and(prePredicate, predicate);//这个是让这两个条件进行and的连接。
                        } else {
                            prePredicate = criteriaBuilder.or(prePredicate, predicate);//这个是让这两个条件进行or的连接。
                        }
                    }
                }
                //将list对象转化为p类型数组，加载cb中
                return prePredicate;
            }
        };
        return specification;
    }

}
